/*
 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * $Id: Sort.java,v 1.2.4.1 2005/09/12 11:08:12 pvedula Exp $
 */

package com.sun.org.apache.xalan.internal.xsltc.compiler;

import java.text.Collator;
import java.util.ArrayList;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.Vector;

import com.sun.org.apache.bcel.internal.classfile.Field;
import com.sun.org.apache.bcel.internal.classfile.Method;
import com.sun.org.apache.bcel.internal.generic.ALOAD;
import com.sun.org.apache.bcel.internal.generic.ANEWARRAY;
import com.sun.org.apache.bcel.internal.generic.ASTORE;
import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
import com.sun.org.apache.bcel.internal.generic.GETFIELD;
import com.sun.org.apache.bcel.internal.generic.ICONST;
import com.sun.org.apache.bcel.internal.generic.ILOAD;
import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
import com.sun.org.apache.bcel.internal.generic.InstructionList;
import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.org.apache.bcel.internal.generic.NOP;
import com.sun.org.apache.bcel.internal.generic.PUSH;
import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
import com.sun.org.apache.bcel.internal.generic.TABLESWITCH;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.CompareGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.IntType;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSortRecordFactGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSortRecordGenerator;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringType;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
import com.sun.org.apache.xml.internal.dtm.Axis;


/**
 * @author Jacek Ambroziak
 * @author Santiago Pericas-Geertsen
 * @author Morten Jorgensen
 */
final class Sort extends Instruction implements Closure {

  private Expression _select;
  private AttributeValue _order;
  private AttributeValue _caseOrder;
  private AttributeValue _dataType;
  private String _lang; // bug! see 26869

  private String _data = null;


  private String _className = null;
  private ArrayList _closureVars = null;
  private boolean _needsSortRecordFactory = false;

  // -- Begin Closure interface --------------------

  /**
   * Returns true if this closure is compiled in an inner class (i.e.
   * if this is a real closure).
   */
  public boolean inInnerClass() {
    return (_className != null);
  }

  /**
   * Returns a reference to its parent closure or null if outermost.
   */
  public Closure getParentClosure() {
    return null;
  }

  /**
   * Returns the name of the auxiliary class or null if this predicate
   * is compiled inside the Translet.
   */
  public String getInnerClassName() {
    return _className;
  }

  /**
   * Add new variable to the closure.
   */
  public void addVariable(VariableRefBase variableRef) {
    if (_closureVars == null) {
      _closureVars = new ArrayList();
    }

    // Only one reference per variable
    if (!_closureVars.contains(variableRef)) {
      _closureVars.add(variableRef);
      _needsSortRecordFactory = true;
    }
  }

  // -- End Closure interface ----------------------

  private void setInnerClassName(String className) {
    _className = className;
  }

  /**
   * Parse the attributes of the xsl:sort element
   */
  public void parseContents(Parser parser) {

    final SyntaxTreeNode parent = getParent();
    if (!(parent instanceof ApplyTemplates) &&
        !(parent instanceof ForEach)) {
      reportError(this, parser, ErrorMsg.STRAY_SORT_ERR, null);
      return;
    }

    // Parse the select expression (node string value if no expression)
    _select = parser.parseExpression(this, "select", "string(.)");

    // Get the sort order; default is 'ascending'
    String val = getAttribute("order");
    if (val.length() == 0) {
      val = "ascending";
    }
    _order = AttributeValue.create(this, val, parser);

    // Get the sort data type; default is text
    val = getAttribute("data-type");
    if (val.length() == 0) {
      try {
        final Type type = _select.typeCheck(parser.getSymbolTable());
        if (type instanceof IntType) {
          val = "number";
        } else {
          val = "text";
        }
      } catch (TypeCheckError e) {
        val = "text";
      }
    }
    _dataType = AttributeValue.create(this, val, parser);

    _lang = getAttribute("lang"); // bug! see 26869
    // val =  getAttribute("lang");
    // _lang = AttributeValue.create(this, val, parser);
    // Get the case order; default is language dependant
    val = getAttribute("case-order");
    _caseOrder = AttributeValue.create(this, val, parser);

  }

  /**
   * Run type checks on the attributes; expression must return a string
   * which we will use as a sort key
   */
  public Type typeCheck(SymbolTable stable) throws TypeCheckError {
    final Type tselect = _select.typeCheck(stable);

    // If the sort data-type is not set we use the natural data-type
    // of the data we will sort
    if (!(tselect instanceof StringType)) {
      _select = new CastExpr(_select, Type.String);
    }

    _order.typeCheck(stable);
    _caseOrder.typeCheck(stable);
    _dataType.typeCheck(stable);
    return Type.Void;
  }

  /**
   * These two methods are needed in the static methods that compile the
   * overloaded NodeSortRecord.compareType() and NodeSortRecord.sortOrder()
   */
  public void translateSortType(ClassGenerator classGen,
      MethodGenerator methodGen) {
    _dataType.translate(classGen, methodGen);
  }

  public void translateSortOrder(ClassGenerator classGen,
      MethodGenerator methodGen) {
    _order.translate(classGen, methodGen);
  }

  public void translateCaseOrder(ClassGenerator classGen,
      MethodGenerator methodGen) {
    _caseOrder.translate(classGen, methodGen);
  }

  public void translateLang(ClassGenerator classGen,
      MethodGenerator methodGen) {
    final ConstantPoolGen cpg = classGen.getConstantPool();
    final InstructionList il = methodGen.getInstructionList();
    il.append(new PUSH(cpg, _lang)); // bug! see 26869
  }

  /**
   * This method compiles code for the select expression for this
   * xsl:sort element. The method is called from the static code-generating
   * methods in this class.
   */
  public void translateSelect(ClassGenerator classGen,
      MethodGenerator methodGen) {
    _select.translate(classGen, methodGen);
  }

  /**
   * This method should not produce any code
   */
  public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
    // empty
  }

  /**
   * Compiles code that instantiates a SortingIterator object.
   * This object's constructor needs referencdes to the current iterator
   * and a node sort record producing objects as its parameters.
   */
  public static void translateSortIterator(ClassGenerator classGen,
      MethodGenerator methodGen,
      Expression nodeSet,
      Vector sortObjects) {
    final ConstantPoolGen cpg = classGen.getConstantPool();
    final InstructionList il = methodGen.getInstructionList();

    // SortingIterator.SortingIterator(NodeIterator,NodeSortRecordFactory);
    final int init = cpg.addMethodref(SORT_ITERATOR, "<init>",
        "("
            + NODE_ITERATOR_SIG
            + NODE_SORT_FACTORY_SIG
            + ")V");

    // Backwards branches are prohibited if an uninitialized object is
    // on the stack by section 4.9.4 of the JVM Specification, 2nd Ed.
    // We don't know whether this code might contain backwards branches
    // so we mustn't create the new object until after we've created
    // the suspect arguments to its constructor.  Instead we calculate
    // the values of the arguments to the constructor first, store them
    // in temporary variables, create the object and reload the
    // arguments from the temporaries to avoid the problem.

    LocalVariableGen nodesTemp =
        methodGen.addLocalVariable("sort_tmp1",
            Util.getJCRefType(NODE_ITERATOR_SIG),
            null, null);

    LocalVariableGen sortRecordFactoryTemp =
        methodGen.addLocalVariable("sort_tmp2",
            Util.getJCRefType(NODE_SORT_FACTORY_SIG),
            null, null);

    // Get the current node iterator
    if (nodeSet == null) {  // apply-templates default
      final int children = cpg.addInterfaceMethodref(DOM_INTF,
          "getAxisIterator",
          "(I)" +
              NODE_ITERATOR_SIG);
      il.append(methodGen.loadDOM());
      il.append(new PUSH(cpg, Axis.CHILD));
      il.append(new INVOKEINTERFACE(children, 2));
    } else {
      nodeSet.translate(classGen, methodGen);
    }

    nodesTemp.setStart(il.append(new ASTORE(nodesTemp.getIndex())));

    // Compile the code for the NodeSortRecord producing class and pass
    // that as the last argument to the SortingIterator constructor.
    compileSortRecordFactory(sortObjects, classGen, methodGen);
    sortRecordFactoryTemp.setStart(
        il.append(new ASTORE(sortRecordFactoryTemp.getIndex())));

    il.append(new NEW(cpg.addClass(SORT_ITERATOR)));
    il.append(DUP);
    nodesTemp.setEnd(il.append(new ALOAD(nodesTemp.getIndex())));
    sortRecordFactoryTemp.setEnd(
        il.append(new ALOAD(sortRecordFactoryTemp.getIndex())));
    il.append(new INVOKESPECIAL(init));
  }


  /**
   * Compiles code that instantiates a NodeSortRecordFactory object which
   * will produce NodeSortRecord objects of a specific type.
   */
  public static void compileSortRecordFactory(Vector sortObjects,
      ClassGenerator classGen, MethodGenerator methodGen) {
    String sortRecordClass =
        compileSortRecord(sortObjects, classGen, methodGen);

    boolean needsSortRecordFactory = false;
    final int nsorts = sortObjects.size();
    for (int i = 0; i < nsorts; i++) {
      final Sort sort = (Sort) sortObjects.elementAt(i);
      needsSortRecordFactory |= sort._needsSortRecordFactory;
    }

    String sortRecordFactoryClass = NODE_SORT_FACTORY;
    if (needsSortRecordFactory) {
      sortRecordFactoryClass =
          compileSortRecordFactory(sortObjects, classGen, methodGen,
              sortRecordClass);
    }

    final ConstantPoolGen cpg = classGen.getConstantPool();
    final InstructionList il = methodGen.getInstructionList();

    // Backwards branches are prohibited if an uninitialized object is
    // on the stack by section 4.9.4 of the JVM Specification, 2nd Ed.
    // We don't know whether this code might contain backwards branches
    // so we mustn't create the new object until after we've created
    // the suspect arguments to its constructor.  Instead we calculate
    // the values of the arguments to the constructor first, store them
    // in temporary variables, create the object and reload the
    // arguments from the temporaries to avoid the problem.

    // Compile code that initializes the static _sortOrder
    LocalVariableGen sortOrderTemp
        = methodGen.addLocalVariable("sort_order_tmp",
        Util.getJCRefType("[" + STRING_SIG),
        null, null);
    il.append(new PUSH(cpg, nsorts));
    il.append(new ANEWARRAY(cpg.addClass(STRING)));
    for (int level = 0; level < nsorts; level++) {
      final Sort sort = (Sort) sortObjects.elementAt(level);
      il.append(DUP);
      il.append(new PUSH(cpg, level));
      sort.translateSortOrder(classGen, methodGen);
      il.append(AASTORE);
    }
    sortOrderTemp.setStart(il.append(new ASTORE(sortOrderTemp.getIndex())));

    LocalVariableGen sortTypeTemp
        = methodGen.addLocalVariable("sort_type_tmp",
        Util.getJCRefType("[" + STRING_SIG),
        null, null);
    il.append(new PUSH(cpg, nsorts));
    il.append(new ANEWARRAY(cpg.addClass(STRING)));
    for (int level = 0; level < nsorts; level++) {
      final Sort sort = (Sort) sortObjects.elementAt(level);
      il.append(DUP);
      il.append(new PUSH(cpg, level));
      sort.translateSortType(classGen, methodGen);
      il.append(AASTORE);
    }
    sortTypeTemp.setStart(il.append(new ASTORE(sortTypeTemp.getIndex())));

    LocalVariableGen sortLangTemp
        = methodGen.addLocalVariable("sort_lang_tmp",
        Util.getJCRefType("[" + STRING_SIG),
        null, null);
    il.append(new PUSH(cpg, nsorts));
    il.append(new ANEWARRAY(cpg.addClass(STRING)));
    for (int level = 0; level < nsorts; level++) {
      final Sort sort = (Sort) sortObjects.elementAt(level);
      il.append(DUP);
      il.append(new PUSH(cpg, level));
      sort.translateLang(classGen, methodGen);
      il.append(AASTORE);
    }
    sortLangTemp.setStart(il.append(new ASTORE(sortLangTemp.getIndex())));

    LocalVariableGen sortCaseOrderTemp
        = methodGen.addLocalVariable("sort_case_order_tmp",
        Util.getJCRefType("[" + STRING_SIG),
        null, null);
    il.append(new PUSH(cpg, nsorts));
    il.append(new ANEWARRAY(cpg.addClass(STRING)));
    for (int level = 0; level < nsorts; level++) {
      final Sort sort = (Sort) sortObjects.elementAt(level);
      il.append(DUP);
      il.append(new PUSH(cpg, level));
      sort.translateCaseOrder(classGen, methodGen);
      il.append(AASTORE);
    }
    sortCaseOrderTemp.setStart(
        il.append(new ASTORE(sortCaseOrderTemp.getIndex())));

    il.append(new NEW(cpg.addClass(sortRecordFactoryClass)));
    il.append(DUP);
    il.append(methodGen.loadDOM());
    il.append(new PUSH(cpg, sortRecordClass));
    il.append(classGen.loadTranslet());

    sortOrderTemp.setEnd(il.append(new ALOAD(sortOrderTemp.getIndex())));
    sortTypeTemp.setEnd(il.append(new ALOAD(sortTypeTemp.getIndex())));
    sortLangTemp.setEnd(il.append(new ALOAD(sortLangTemp.getIndex())));
    sortCaseOrderTemp.setEnd(
        il.append(new ALOAD(sortCaseOrderTemp.getIndex())));

    il.append(new INVOKESPECIAL(
        cpg.addMethodref(sortRecordFactoryClass, "<init>",
            "(" + DOM_INTF_SIG
                + STRING_SIG
                + TRANSLET_INTF_SIG
                + "[" + STRING_SIG
                + "[" + STRING_SIG
                + "[" + STRING_SIG
                + "[" + STRING_SIG + ")V")));

    // Initialize closure variables in sortRecordFactory
    final ArrayList dups = new ArrayList();

    for (int j = 0; j < nsorts; j++) {
      final Sort sort = (Sort) sortObjects.get(j);
      final int length = (sort._closureVars == null) ? 0 :
          sort._closureVars.size();

      for (int i = 0; i < length; i++) {
        VariableRefBase varRef = (VariableRefBase) sort._closureVars.get(i);

        // Discard duplicate variable references
        if (dups.contains(varRef)) {
          continue;
        }

        final VariableBase var = varRef.getVariable();

        // Store variable in new closure
        il.append(DUP);
        il.append(var.loadInstruction());
        il.append(new PUTFIELD(
            cpg.addFieldref(sortRecordFactoryClass, var.getEscapedName(),
                var.getType().toSignature())));
        dups.add(varRef);
      }
    }
  }

  public static String compileSortRecordFactory(Vector sortObjects,
      ClassGenerator classGen, MethodGenerator methodGen,
      String sortRecordClass) {
    final XSLTC xsltc = ((Sort) sortObjects.firstElement()).getXSLTC();
    final String className = xsltc.getHelperClassName();

    final NodeSortRecordFactGenerator sortRecordFactory =
        new NodeSortRecordFactGenerator(className,
            NODE_SORT_FACTORY,
            className + ".java",
            ACC_PUBLIC | ACC_SUPER | ACC_FINAL,
            new String[]{},
            classGen.getStylesheet());

    ConstantPoolGen cpg = sortRecordFactory.getConstantPool();

    // Add a new instance variable for each var in closure
    final int nsorts = sortObjects.size();
    final ArrayList dups = new ArrayList();

    for (int j = 0; j < nsorts; j++) {
      final Sort sort = (Sort) sortObjects.get(j);
      final int length = (sort._closureVars == null) ? 0 :
          sort._closureVars.size();

      for (int i = 0; i < length; i++) {
        final VariableRefBase varRef = (VariableRefBase) sort._closureVars.get(i);

        // Discard duplicate variable references
        if (dups.contains(varRef)) {
          continue;
        }

        final VariableBase var = varRef.getVariable();
        sortRecordFactory.addField(new Field(ACC_PUBLIC,
            cpg.addUtf8(var.getEscapedName()),
            cpg.addUtf8(var.getType().toSignature()),
            null, cpg.getConstantPool()));
        dups.add(varRef);
      }
    }

    // Define a constructor for this class
    final com.sun.org.apache.bcel.internal.generic.Type[] argTypes =
        new com.sun.org.apache.bcel.internal.generic.Type[7];
    argTypes[0] = Util.getJCRefType(DOM_INTF_SIG);
    argTypes[1] = Util.getJCRefType(STRING_SIG);
    argTypes[2] = Util.getJCRefType(TRANSLET_INTF_SIG);
    argTypes[3] = Util.getJCRefType("[" + STRING_SIG);
    argTypes[4] = Util.getJCRefType("[" + STRING_SIG);
    argTypes[5] = Util.getJCRefType("[" + STRING_SIG);
    argTypes[6] = Util.getJCRefType("[" + STRING_SIG);

    final String[] argNames = new String[7];
    argNames[0] = DOCUMENT_PNAME;
    argNames[1] = "className";
    argNames[2] = TRANSLET_PNAME;
    argNames[3] = "order";
    argNames[4] = "type";
    argNames[5] = "lang";
    argNames[6] = "case_order";

    InstructionList il = new InstructionList();
    final MethodGenerator constructor =
        new MethodGenerator(ACC_PUBLIC,
            com.sun.org.apache.bcel.internal.generic.Type.VOID,
            argTypes, argNames, "<init>",
            className, il, cpg);

    // Push all parameters onto the stack and called super.<init>()
    il.append(ALOAD_0);
    il.append(ALOAD_1);
    il.append(ALOAD_2);
    il.append(new ALOAD(3));
    il.append(new ALOAD(4));
    il.append(new ALOAD(5));
    il.append(new ALOAD(6));
    il.append(new ALOAD(7));
    il.append(new INVOKESPECIAL(cpg.addMethodref(NODE_SORT_FACTORY,
        "<init>",
        "(" + DOM_INTF_SIG
            + STRING_SIG
            + TRANSLET_INTF_SIG
            + "[" + STRING_SIG
            + "[" + STRING_SIG
            + "[" + STRING_SIG
            + "[" + STRING_SIG + ")V")));
    il.append(RETURN);

    // Override the definition of makeNodeSortRecord()
    il = new InstructionList();
    final MethodGenerator makeNodeSortRecord =
        new MethodGenerator(ACC_PUBLIC,
            Util.getJCRefType(NODE_SORT_RECORD_SIG),
            new com.sun.org.apache.bcel.internal.generic.Type[]{
                com.sun.org.apache.bcel.internal.generic.Type.INT,
                com.sun.org.apache.bcel.internal.generic.Type.INT},
            new String[]{"node", "last"}, "makeNodeSortRecord",
            className, il, cpg);

    il.append(ALOAD_0);
    il.append(ILOAD_1);
    il.append(ILOAD_2);
    il.append(new INVOKESPECIAL(cpg.addMethodref(NODE_SORT_FACTORY,
        "makeNodeSortRecord", "(II)" + NODE_SORT_RECORD_SIG)));
    il.append(DUP);
    il.append(new CHECKCAST(cpg.addClass(sortRecordClass)));

    // Initialize closure in record class
    final int ndups = dups.size();
    for (int i = 0; i < ndups; i++) {
      final VariableRefBase varRef = (VariableRefBase) dups.get(i);
      final VariableBase var = varRef.getVariable();
      final Type varType = var.getType();

      il.append(DUP);

      // Get field from factory class
      il.append(ALOAD_0);
      il.append(new GETFIELD(
          cpg.addFieldref(className,
              var.getEscapedName(), varType.toSignature())));

      // Put field in record class
      il.append(new PUTFIELD(
          cpg.addFieldref(sortRecordClass,
              var.getEscapedName(), varType.toSignature())));
    }
    il.append(POP);
    il.append(ARETURN);

    constructor.setMaxLocals();
    constructor.setMaxStack();
    sortRecordFactory.addMethod(constructor);
    makeNodeSortRecord.setMaxLocals();
    makeNodeSortRecord.setMaxStack();
    sortRecordFactory.addMethod(makeNodeSortRecord);
    xsltc.dumpClass(sortRecordFactory.getJavaClass());

    return className;
  }

  /**
   * Create a new auxillary class extending NodeSortRecord.
   */
  private static String compileSortRecord(Vector sortObjects,
      ClassGenerator classGen,
      MethodGenerator methodGen) {
    final XSLTC xsltc = ((Sort) sortObjects.firstElement()).getXSLTC();
    final String className = xsltc.getHelperClassName();

    // This generates a new class for handling this specific sort
    final NodeSortRecordGenerator sortRecord =
        new NodeSortRecordGenerator(className,
            NODE_SORT_RECORD,
            "sort$0.java",
            ACC_PUBLIC | ACC_SUPER | ACC_FINAL,
            new String[]{},
            classGen.getStylesheet());

    final ConstantPoolGen cpg = sortRecord.getConstantPool();

    // Add a new instance variable for each var in closure
    final int nsorts = sortObjects.size();
    final ArrayList dups = new ArrayList();

    for (int j = 0; j < nsorts; j++) {
      final Sort sort = (Sort) sortObjects.get(j);

      // Set the name of the inner class in this sort object
      sort.setInnerClassName(className);

      final int length = (sort._closureVars == null) ? 0 :
          sort._closureVars.size();
      for (int i = 0; i < length; i++) {
        final VariableRefBase varRef = (VariableRefBase) sort._closureVars.get(i);

        // Discard duplicate variable references
        if (dups.contains(varRef)) {
          continue;
        }

        final VariableBase var = varRef.getVariable();
        sortRecord.addField(new Field(ACC_PUBLIC,
            cpg.addUtf8(var.getEscapedName()),
            cpg.addUtf8(var.getType().toSignature()),
            null, cpg.getConstantPool()));
        dups.add(varRef);
      }
    }

    MethodGenerator init = compileInit(sortObjects, sortRecord,
        cpg, className);
    MethodGenerator extract = compileExtract(sortObjects, sortRecord,
        cpg, className);
    sortRecord.addMethod(init);
    sortRecord.addMethod(extract);

    xsltc.dumpClass(sortRecord.getJavaClass());
    return className;
  }

  /**
   * Create a constructor for the new class. Updates the reference to the
   * collator in the super calls only when the stylesheet specifies a new
   * language in xsl:sort.
   */
  private static MethodGenerator compileInit(Vector sortObjects,
      NodeSortRecordGenerator sortRecord,
      ConstantPoolGen cpg,
      String className) {
    final InstructionList il = new InstructionList();
    final MethodGenerator init =
        new MethodGenerator(ACC_PUBLIC,
            com.sun.org.apache.bcel.internal.generic.Type.VOID,
            null, null, "<init>", className,
            il, cpg);

    // Call the constructor in the NodeSortRecord superclass
    il.append(ALOAD_0);
    il.append(new INVOKESPECIAL(cpg.addMethodref(NODE_SORT_RECORD,
        "<init>", "()V")));

    il.append(RETURN);

    return init;
  }


  /**
   * Compiles a method that overloads NodeSortRecord.extractValueFromDOM()
   */
  private static MethodGenerator compileExtract(Vector sortObjects,
      NodeSortRecordGenerator sortRecord,
      ConstantPoolGen cpg,
      String className) {
    final InstructionList il = new InstructionList();

    // String NodeSortRecord.extractValueFromDOM(dom,node,level);
    final CompareGenerator extractMethod =
        new CompareGenerator(ACC_PUBLIC | ACC_FINAL,
            com.sun.org.apache.bcel.internal.generic.Type.STRING,
            new com.sun.org.apache.bcel.internal.generic.Type[]{
                Util.getJCRefType(DOM_INTF_SIG),
                com.sun.org.apache.bcel.internal.generic.Type.INT,
                com.sun.org.apache.bcel.internal.generic.Type.INT,
                Util.getJCRefType(TRANSLET_SIG),
                com.sun.org.apache.bcel.internal.generic.Type.INT
            },
            new String[]{"dom",
                "current",
                "level",
                "translet",
                "last"
            },
            "extractValueFromDOM", className, il, cpg);

    // Values needed for the switch statement
    final int levels = sortObjects.size();
    final int match[] = new int[levels];
    final InstructionHandle target[] = new InstructionHandle[levels];
    InstructionHandle tblswitch = null;

    // Compile switch statement only if the key has multiple levels
    if (levels > 1) {
      // Put the parameter to the swtich statement on the stack
      il.append(new ILOAD(extractMethod.getLocalIndex("level")));
      // Append the switch statement here later on
      tblswitch = il.append(new NOP());
    }

    // Append all the cases for the switch statment
    for (int level = 0; level < levels; level++) {
      match[level] = level;
      final Sort sort = (Sort) sortObjects.elementAt(level);
      target[level] = il.append(NOP);
      sort.translateSelect(sortRecord, extractMethod);
      il.append(ARETURN);
    }

    // Compile def. target for switch statement if key has multiple levels
    if (levels > 1) {
      // Append the default target - it will _NEVER_ be reached
      InstructionHandle defaultTarget =
          il.append(new PUSH(cpg, EMPTYSTRING));
      il.insert(tblswitch, new TABLESWITCH(match, target, defaultTarget));
      il.append(ARETURN);
    }

    return extractMethod;
  }
}
